package com.hhwy.framework.container;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;

import com.hhwy.framework.annotation.RestDesc;
import com.hhwy.framework.common.Constants;
import com.hhwy.framework.mvc.RestInfo;
import com.hhwy.framework.persistent.DAO;
import com.hhwy.framework.util.IoTools;
import com.hhwy.framework.util.JsonUtils;

/**
 * <b>类 名 称：</b>AppContainer<br/>
 * <b>类 描 述：</b>系统容器，初始化插件自定义的spring配置；提供获取所有spring管理的bean的接口<br/>
 * <b>创 建 人：</b>lihuiming<br/>
 * <b>修 改 人：</b>Administrator<br/>
 * <b>修改时间：</b>2014年8月4日 上午9:46:24<br/>
 * <b>修改备注：</b><br/>
 * 
 * @version 1.0.0<br/>
 */
public final class AppContainer {
	public static final String KEY_WEBCONTEXT = "webContext";
	public static final String KEY_PLUGIN_CONTEXT = "pluginContext";
	static Logger log = LoggerFactory.getLogger(AppContainer.class);
	static WebApplicationContext webContext;
	static WebApplicationContext pluginContext = null;
	private static String artifactId = null;
	
	static Map<String,ApplicationContext> contextNameMap = new HashMap<String,ApplicationContext>();
	static Resource[] contextResources = null;
	static {
		contextNameMap.put(KEY_WEBCONTEXT, webContext);
		contextNameMap.put(KEY_PLUGIN_CONTEXT, pluginContext);
		artifactId = getArtifactId();
	}
	

	public static ApplicationContext getWebContext(){
		
		return webContext;
	}
	
	public static ApplicationContext getPluginContext(){
		
		return pluginContext;
	}
	
	public static DAO<?> getDao(){
		//开发模式使用主框架的dao
		if(!Constants.INIT_PLUGIN_MAPPING_NAME.equals(artifactId) || pluginContext == null ){
			if(webContext == null){
				return null;
			}
			return webContext.getBean(DAO.class);
		}else{
			return pluginContext.getBean(DAO.class);
		}
	}

	/**
	 * getResutAPIs(获取系统或插件的REST接口)<br/>
	 * 
	 * @param pluginName
	 * @return String
	 * @exception
	 * @since 1.0.0
	 */
	public static String getResutAPIs(String pluginName) {
		/** REST接口信息 */
		List<RestInfo> restInfos = new ArrayList<>();
		if(pluginContext != null)
			getResutAPIs(pluginContext, restInfos);
		else
			getResutAPIs(webContext, restInfos);
			
		return JsonUtils.toJson(restInfos);

	}

	/**
	 * getResutAPIs(获取全部的Rest接口描述)<br/>
	 * 
	 * @return List<RestInfo>
	 * @exception
	 * @since 1.0.0
	 */
	public static List<RestInfo> getResutAPIs(WebApplicationContext webContext,
			List<RestInfo> restInfos) {

		Map<String, Object> controllers = webContext
				.getBeansWithAnnotation(RestController.class);

		Iterator<String> it = controllers.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			Object controller = controllers.get(key);

			List<RestInfo> ri = getRestInfo(controller);
			restInfos.addAll(ri);
		}
		Collections.sort(restInfos);

		return restInfos;
	}
	/**
	 * getArtifactId: 读取pom.xml的artifactId作为开发工程访问路径，保证开发模式下当前工程的请求访问主容器<br/>
	 * @return 
	 * String
	 * @exception 
	 * @since  1.0.0
	 */
	public static String getArtifactId(){
		if(artifactId != null)
			return artifactId;
		
		 MavenXpp3Reader reader = new MavenXpp3Reader();
		 int index = IoTools.getABSPATH().indexOf("/src/");
	     String myPom = IoTools.getABSPATH().substring(0,index+1) + "pom.xml";
	     try {
	    	File pomFile = new File(myPom);
	    	//没有pom.xml，认为是发布运行状态，默认访问code,定向到插件容器
	    	if(!pomFile.isFile())
	    		return Constants.INIT_PLUGIN_MAPPING_NAME;
	    	//
			Model model = reader.read(new FileReader(pomFile));
			artifactId = model.getArtifactId();
			
			
		} catch (IOException | XmlPullParserException e) {
			e.printStackTrace();
		}
	    return artifactId;
	}
	/**
	 * getRestInfo(读取controller信息)<br/>
	 * @param controller
	 * @return RestInfo
	 * @exception
	 * @since 1.0.0
	 */
	private static List<RestInfo> getRestInfo(Object controller) {

		String url = getClassUrl(controller);
		List<RestInfo> restInfoList = new ArrayList<RestInfo>();
		Method[] methods = controller.getClass().getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			Annotation[] annos = methods[i].getAnnotations();
			RestInfo ri = new RestInfo();
			for (int j = 0; j < annos.length; j++) {
				if (annos[j].annotationType().equals(RequestMapping.class)) {
					ri.setClassName(controller.getClass().getSimpleName());
					RequestMapping rm = (RequestMapping) annos[j];
					String[] urlValues = rm.value();
					if (urlValues.length > 0)
						ri.setUrl(url + getUrl(rm.value()));
					else
						ri.setUrl(url + "/");

					if (rm.method().length > 0)
						ri.setMethod(getMethod(rm.method()));
				}
				if (annos[j].annotationType().equals(RestDesc.class)) {
					RestDesc rd = (RestDesc) annos[j];
					ri.setDesc(rd.value().toString());
					ri.setParams(rd.params());
				}
			}
			if (ri.getUrl() != null)
				restInfoList.add(ri);
		}

		return restInfoList;
	}

	/**
	 * getClassUrl(描述这个方法的作用)<br/>
	 * 
	 * @param controller
	 * @return String
	 * @exception
	 * @since 1.0.0
	 */
	private static String getClassUrl(Object controller) {
		Annotation[] annos = controller.getClass().getAnnotations();
		String url = null;
		for (int i = 0; i < annos.length; i++) {
			if (annos[i].annotationType().equals(RequestMapping.class)) {
				url = ((RequestMapping) annos[i]).value()[0].toString();
				break;
			}
		}
		return url;
	}

	/**
	 * getMethod(获取Rest接口限定的方法[get,post,put,delete])<br/>
	 * 
	 * @param method
	 * @return String
	 * @exception
	 * @since 1.0.0
	 */
	private static String getMethod(RequestMethod[] method) {
		StringBuffer sbf = new StringBuffer();
		for (int i = 0; i < method.length; i++) {
			sbf.append(method[i].toString());
			if (i < method.length - 1)
				sbf.append(",");
		}
		return sbf.toString();
	}

	/**
	 * getUrl(获取Rest接口的url数组)<br/>
	 * 
	 * @param value
	 * @return String
	 * @exception
	 * @since 1.0.0
	 */
	private static String getUrl(String[] value) {
		StringBuffer sbf = new StringBuffer();
		for (int i = 0; i < value.length; i++) {
			sbf.append(value[i].toString());
			if (i < value.length - 1)
				sbf.append(",");
		}
		return sbf.toString();
	}

	/**
	 * loadContext(描述这个方法的作用)<br/>
	 * 
	 * @param contextResources
	 *            void
	 * @throws IOException
	 * @exception
	 * @since 1.0.0
	 */
	@SuppressWarnings("unused")
	private static FileSystemXmlApplicationContext loadContext(Object[] contextResourcesIn) {

		List<String> contextList = new ArrayList<>();
		for (int i = 0; i < contextResourcesIn.length; i++) {
			Resource resource = (Resource)contextResourcesIn[i];
			try {
				if (resource.getURL().toString().startsWith("file:"))
					contextList.add(resource.getFile().getPath().toString());
				else
					contextList.add(resource.getURI().toString());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (contextList.size() > 0) {
			String[] contexts = new String[contextList.size()];
			ApplicationContext parent = webContext;
			return new FileSystemXmlApplicationContext(
					contextList.toArray(contexts), parent);
		}else
			return null;
	}

	public static void setWebContext(WebApplicationContext context) {
			webContext = context;
			contextNameMap.put(KEY_WEBCONTEXT, webContext);
	}

	/**
	 * addPluginContext(添加插件容器)<br/>
	 * 
	 * @param pluginContext
	 *            void
	 * @exception
	 * @since 1.0.0
	 */
	public static void addPluginContext(String pluginName,
			WebApplicationContext pluginContextIn) {

		pluginContext = pluginContextIn;
		contextNameMap.put(KEY_PLUGIN_CONTEXT, pluginContext);
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String beanName) {

		Object obj = null;
		if (obj == null) {
			if (pluginContext != null && pluginContext.containsBean(beanName)) {
				obj = pluginContext.getBean(beanName);
			}
		}
		if (obj == null) {
			try {
				if (webContext != null && webContext.containsBean(beanName)) {
					obj = webContext.getBean(beanName);
				}
			} catch (Exception e) {
			}

		}
		//处理容器刷新时，旧的容器尚未销毁，新的容器尚未构建成功时，获取bean的情况
		/*if(obj != null){
			URLClassLoader tempClassLoader = appContainer.getTempBusinessClassLoader();
			if(tempClassLoader != null){
				ClassLoader objClassLoader = obj.getClass().getClassLoader();
				if(!objClassLoader.equals(tempClassLoader) && !objClassLoader.equals(tempClassLoader.getParent())){
					return null;
				}
			}
		}*/
		return (T) obj;
	}

	/**
	 * getBeanFromMVContainer(从MVC容器中获取bean)<br/>
	 * 
	 * @param clazz
	 * @return T
	 * @exception
	 * @since 1.0.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBeanFromMVContainer(Class<?> clazz) {
		Object obj = null;
		try {
			if (webContext != null) {
				obj = webContext.getBean(clazz);
				return (T) obj;
			} else
				return null;
		} catch (Exception e) {
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBeanFromPluginContext(Class<?> clazz) {
		try {
			if (pluginContext != null)
				return (T) pluginContext.getBean(clazz);
		} catch (Exception e) {
		}
		return null;
	}

	public static <T> T getBean(Class<?> clazz) {

		return getRepository(clazz);
	}
	/**
	 * getBeansByType 从核心容器获取同一类型的所有bean<br/>
	 * @param clazz
	 * @return 
	 * List<Object>
	 * @exception 
	 * @since  1.0.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<T> getBeansByType(Class<?> clazz){
		List<T> beanList = new ArrayList<T>();
		@SuppressWarnings("unused")
		Map<String, ?> beans = null;
		// 从插件容器中获取
		try {
			if (pluginContext != null) {
				beanList.addAll((Collection<T>)pluginContext.getBeansOfType(clazz).values());
			}
		} catch (Exception e) {
		}
		try {
			if (webContext != null) {
				beanList.addAll((Collection<T>)webContext.getBeansOfType(clazz).values());
			}
		} catch (Exception e) {
		}
		return beanList;
	}
	/**
	 * 获取仓库
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getRepository(Class<?> clazz) {

		Object obj = null;
		
		// 从插件容器中获取
		if (obj == null) {
			try {
				if (pluginContext != null)
					obj = pluginContext.getBean(clazz);
			} catch (Exception e) {
				log.warn("pluginContext中未找到【"+clazz.getSimpleName()+"】");
			}
		}
		try {
			if (obj == null && webContext != null) {
				obj = webContext.getBean(clazz);
			}
		} catch (Exception e) {
			log.warn("webContext中未找到【"+clazz.getSimpleName()+"】");
		}
		return (T) obj;
	}

	/**
	 * getBeanNames(描述这个方法的作用)<br/>
	 * @param containerName
	 * @return 
	 * Object
	 * @exception 
	 * @since  1.0.0
	*/
	public static Object getBeanNames(String containerName) {
		if (contextNameMap.get(containerName) != null) {
			String[] beanNames = contextNameMap.get(containerName).getBeanDefinitionNames();
			for (String name : beanNames) {
				System.out.println(name + ":" + contextNameMap.get(containerName).getBean(name).getClass() + ":" + contextNameMap.get(containerName).getBean(name) + ":" + contextNameMap.get(containerName).getBean(name).hashCode());
			}
			return beanNames;
		}

		return null;
	}
	
	/**
	 * getApplicationContextMap(获取所有的容器)<br/>
	 * @return 
	 * Map<String,ApplicationContext>
	 * @exception 
	 * @since  1.0.0
	 */
	public static Map<String,ApplicationContext> getApplicationContextMap() {
		
		return contextNameMap;
	}

}
